Uploading to Facebook

Latest update: May 2015

In this tutorial, we’ll go over uploading to Facebook. We have already released a sample Facebook script, so instead of creating a new one we’re going to go over how it works.

Facebook.html

Facebook.html uses javascript to redirect the user to Facebook’s OAuth service. It’s just used to get the Authentication Token.

fb_get_token.lua

This script is used to get the "long term" access token (the one that lasts 60 days, as opposed to only 1 hour).

First, it uses fa.request and cjson to confirm that the current access token (the 1 hour version) is still valid.

local function hasValidToken(t)
    local b, c, h = fa.request {
        url = 'https://graph.facebook.com/debug_token?input_token='
            ..t.user_access_token..'&access_token='
            ..t.app_access_token,
        headers = {Connection = 'close'}
    }
    if (c == 200) then
        local cjson = require("cjson")
        local res = cjson.decode(b)
        cjson = nil
        if (res.error ~= nil) then
            t.message = res.error.message
        else
            t.long_term = res.data.issued_at
            if (res.data.error ~= nil) then
                t.message = res.data.error.message
            else
                t.message = res.data.message
            end
        end
        return res.data.is_valid
    else
        t.message = b
        return false
    end
end

It checks the response code (c) first, and if it’s "200" (a success) will move on to deconding the JSON response. If any of these steps fail, the user does not have a valid token, and "showAuthorizationURL()" will be called - which prints a formatted string directing the user to get a new key.

If hasValidToken returns "true", we move on to requesting the long term token. Here we again use fa.request to call facebook’s OAuth API. We pass out app’s token as well as the temporary access token, and it (should) reply with the 60 day version (which is stored and later printed for the user).

local function getLongtermToken(t)
  local b, c, h = fa.request {
    url = 'https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token'
      ..'&client_id='..t.app_id
      ..'&client_secret='..t.app_secret
      ..'&fb_exchange_token='..t.user_access_token,
    headers = {Connection = 'close'}
  }

  if (c == 200) then
    local s, e = b:find("&")
    local longtermToken = b:sub(1, s - 1)
    local file = io.open( t.token_fullpath, "w" )
    file:write( longtermToken )
    io.close( file )
    file = nil

    s, e = longtermToken:find("=")
    -- Store the returned token
    t.user_access_token = longtermToken:sub(e+1, #longtermToken)
  end
end

fb_token_handler.lua

This script manages reading the config file (facebook.cfg) for fb_get_token.lua. It returns a table (mod) containing the results.

fb_auto_up.lua

Finally, this script uploads to Facebook. First, it waits for WiFi to be connected (waitWlanConnect()), and when it is connected goes through each folder in the upload directory and uploads them one file at a time.

LuaFileSystem is used to find and select each file (autoUpload()).

--...
--For each directory...
for aDirectory in lfs.dir("/DCIM") do
        local _dir_id = tonumber(aDirectory:sub(1, 3))
--        print("DIR:"..aDirectory, lfs.attributes("/DCIM/"..aDirectory, "modification"))
        if (_dir_id ~= nil and _dir_id >= lastDirectory) then
	    --for each file in that directory...
            for aFile in lfs.dir("/DCIM/"..aDirectory) do
	        --Process and upload!
--...

Next, before uploading a folder it uses Facebook’s Graph API to create an album. This is done with a basic fa.request call, then it decodes the response string to get the album ID (which we need to make sure we upload to the right place).

local function createAlbum(_name, _message)
    local req = {}
    req["url"] = 'https://graph.facebook.com/v2.1/me/albums?access_token='..user_access_token
        ..'&name='..string.gsub (_name, " ", "+")
        ..'&message='..string.gsub (_message, " ", "+")
        ..'&privacy=%7B%22value%22%3A+%22SELF%22%7D'
    req["method"] = 'POST'
    req["headers"] = {Connection = "close"}

    local b,c,h = fa.request(req)
    if (c ~= 200) then
        print(h, b)
        return nil
    end
    req = nil

    c, h = nil
    local cjson = require("cjson")
    local res = cjson.decode(b)
    collectgarbage()
    return res["id"]
end

Finally, each file is uploaded (uploadFile(filePath, _album_id)) using fa.request to send a POST request.

local function uploadFile(filePath, _album_id)
    local boundary = '--bnfDxpKY69NKk'
    local headers = {}
    local place_holder = '<!--WLANSDFILE-->'

    headers['Connection'] = 'close'
    headers['Content-Type'] = 'multipart/form-data; boundary="'..boundary..'"'

    local body = '--'..boundary..'\r\n'
        ..'Content-Disposition: form-data; name="source"; filename="'
        ..filePath..'"\r\n'
        ..'Content-Type: image/jpeg\r\n\r\n'
        ..'<!--WLANSDFILE-->\r\n'
        .. '--' .. boundary .. '--\r\n'

    headers['Content-Length'] =
        lfs.attributes(filePath, 'size')
        + string.len(body)
        - string.len(place_holder)

    local args = {}
    args["url"] = 'https://graph.facebook.com/v2.1/'
            .._album_id..'/photos?access_token='
            ..user_access_token
            ..'&message='..filePath
    args["method"] = "POST"
    args["headers"] = headers
    args["body"] = body
    args["file"] = filePath
    args["bufsize"] = 1460*10
    local b,c,h = fa.request(args)
    b, h = nil
    collectgarbage()
    if (c ~= 200) then
        print(h, b)
        return nil
    end
    return c
end

Uploading can take quite some time!

All sample code on this page is licensed under BSD 2-Clause License